import copy
import logging
import os
import threading
import time

import openstack
from flask import current_app
from flask_restx import abort

from app.common.range_manager import RangeManager
from app.models import rangeModel, taasModel, switchModel, VmType
from tools import db, ops
from utils.crypto import decrypt_pw
from utils.utils import init_opserver_conn

# flake8: noqa


class RangeVmBase:
    def __init__(self):
        self.conn = init_opserver_conn()

    def delRange(self, range_uuid):
        with db.auto_commit():
            rangeInfo = rangeModel.RangeLinkModule.query.filter_by(
                range_uuid=range_uuid
            ).first()
            if rangeInfo is None:
                abort(400, message="参数错误")
            rangeModel.Range.query.filter_by(id=rangeInfo.range_id).delete()
            print(range_uuid, "靶场删除成功")

    def clearCoursewareRange(self):
        return
        # 查询课件类型的靶场
        range_cw = rangeModel.Range.query.filter(rangeModel.Range.type == 2).all()

        if not range_cw:
            return

        try:
            for rw in range_cw:

                range_uuid = rw.rangeVmList[0].range_uuid

                # 通过靶场获取活动的虚拟机
                op_vm = rangeModel.RangeVm.query.filter(
                    rangeModel.RangeVm.range_uuid == range_uuid
                ).all()

                if op_vm:
                    for opv in op_vm:
                        if opv.op_vm_uuid:
                            with db.auto_commit():
                                self.conn.delete_server(opv.op_vm_uuid)
                                rangeModel.RangeVm.query.filter_by(id=opv.id).update(
                                    {"op_vm_uuid": None}
                                )
                                rangeModel.db.session.commit()
                                print("删除", opv.op_vm_uuid)
                                time.sleep(3)
                            # 删除虚拟机
                else:
                    # 当课件下面无虚拟机的时候，删除此课件关联的靶场
                    self.delRange(range_uuid)
                    time.sleep(3)

        except Exception as e:
            abort(500, message="无法连接到云计算服务")

    def restoreFactorySettings(self):
        # return True
        try:
            # 删除实例主机
            # servers = self.conn.compute.servers()
            # for server in servers:
            #     print("delete：" + server.id + "...")
            #     self.conn.compute.delete_server(server)
            #     current_app.logger.info(server.id + "---action--restoreFactorySettings")
            #     print("delete ok：" + server.id)
            range_all = rangeModel.Range.query.filter(
                rangeModel.Range.is_system == 0
            ).all()
            for range in range_all:
                range_uuid = range.rangeVmList[0].range_uuid
                range_manager = RangeManager(range_uuid=range_uuid)
                myRangeInfo: rangeModel.RangeLinkModule = range_manager.range_model
                rangeInfo = myRangeInfo.Range
                if rangeInfo:
                    if myRangeInfo.vmList:
                        for vm in myRangeInfo.vmList:
                            if vm.op_vm_uuid:
                                if vm.type == VmType.container:
                                    print("delete：" + vm.op_vm_uuid + "...")
                                    ops.zun_operate.del_container(vm.op_vm_uuid, stop=True)
                                else:
                                    print("delete：" + vm.op_vm_uuid + "...")
                                    self.conn.delete_server(vm.op_vm_uuid)
                                print("delete ok：" + vm.op_vm_uuid)
                                # 判断如果有快照，也一并删除
                                if vm.image_snapshot:
                                    self.conn.delete_image(vm.image_snapshot)
            # 删除网络
            print("start delete network")
            nets = self.conn.network.networks()
            for net in nets:
                # 不删除外网网络
                if net.name == "external":
                    continue
                print("delete：" + net.id + "...")
                try:
                    self.conn.network.delete_network(net)
                except Exception as e:
                    continue
                print("delete ok：" + net.id)

            # # 删除路由
            # print("start delete router")
            # routers = self.conn.network.routers()
            # for router in routers:
            #     print(router)
            #     if router.external_gateway_info:
            #         for subnet in router.external_gateway_info['external_fixed_ips']:
            #             self.conn.network.remove_interface_from_router(router,subnet_id=subnet['subnet_id'])
            #     print("delete："+router.id+"...")
            #     self.conn.network.delete_router(router)
            #     current_app.logger.info(router.id + "---action--restoreFactorySettings")
            #     print("delete ok：" + router.id

            # 删除虚拟机
            range_all = rangeModel.Range.query.filter(
                rangeModel.Range.is_system == 0
            ).all()
            for range in range_all:
                try:
                    range_uuid = range.rangeVmList[0].range_uuid
                    if range_uuid:
                        self.delRange(range_uuid)
                except:
                    pass
            return True

        except Exception as e:
            current_app.logger.exception(e)
            return False

    def sync_op_vm(self):
        self.initLog("sync_op_vm")
        # 靶场虚拟机
        op_vm = rangeModel.RangeVm.query.filter(
            rangeModel.RangeVm.op_vm_uuid != ""
        ).all()

        # 用户启动虚拟机
        op_active_vm = rangeModel.ActiveRangeVm.query.filter(
            rangeModel.ActiveRangeVm.op_vm_uuid != ""
        ).all()
        vm_list = []
        for vm in op_vm:
            vm_list.append(vm.op_vm_uuid)
        for v in op_active_vm:
            vm_list.append(v.op_vm_uuid)
        # openstack 所有虚拟机
        servers = self.conn.compute.servers()

        servers_list = []

        for op_vm in servers:
            servers_list.append(op_vm.id)

        # 找出问题虚机
        diff_list = set(servers_list).difference(set(vm_list))
        # 执行删除虚拟机任务
        if len(diff_list) > 0:
            for vv in diff_list:
                t = threading.Thread(target=self.deleteFun, args=(vv,))
                t.start()  # 启动线程

    def sync_op_net(self):
        self.initLog("sync_op_net")
        # 靶场虚拟机
        op_net = switchModel.Switchs.query.all()

        # 用户启动虚拟机
        op_active_net = switchModel.ActiveSwitchs.query.filter(
            switchModel.ActiveSwitchs.network_id != ""
        ).all()
        net_list = []
        for nt in op_net:
            net_list.append(nt.network_id)
        for v in op_active_net:
            net_list.append(v.network_id)

        # openstack 所有虚拟机

        networks = self.conn.network.networks()
        networks_list = []
        ports_down = {}
        for o in networks:
            if o.is_router_external is True:
                continue
            ports_down[o.id] = []
            for n in self.conn.network.get_subnet_ports(o.subnet_ids[0]):
                if not n.device_owner:
                    ports_down[o.id].append(n.id)
            networks_list.append(o.id)

        # 找出问题虚机
        diff_list = set(networks_list).difference(set(net_list))
        # 执行删除虚拟机任务
        if len(diff_list) > 0:
            for vv in diff_list:
                if ports_down[vv]:
                    for p in ports_down[vv]:
                        self.deletePort(p)
                t = threading.Thread(target=self.deleteNet, args=(vv,))
                t.start()  # 启动线程

    def deletePort(self, port_id):
        self.conn.delete_port(port_id)
        self.logger.info(str(port_id) + "--net--deleted")

    def deleteFun(self, vm_uuid):
        self.conn.delete_server(vm_uuid)
        self.logger.info(str(vm_uuid) + "--deleted")

    def deleteNet(self, net_id):
        self.conn.delete_network(net_id)
        self.logger.info(str(net_id) + "--deleted")

    def initLog(self, filename):
        # 第一步，创建一个logger
        self.logger = logging.getLogger()
        self.logger.setLevel(logging.INFO)  # Log等级总开关
        # 第二步，创建一个handler，用于写入日志文件
        rq = filename + "--" + time.strftime("%Y%m%d", time.localtime(time.time()))
        log_path = os.getcwd() + "/logs/"
        log_name = log_path + rq + ".log"
        logfile = log_name
        fh = logging.FileHandler(logfile, mode="a")
        fh.setLevel(logging.DEBUG)  # 输出到file的log等级的开关
        # 第三步，定义handler的输出格式
        formatter = logging.Formatter(
            "%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s"
        )
        fh.setFormatter(formatter)
        # 第四步，将logger添加到handler里面
        self.logger.addHandler(fh)


class Synctap(RangeVmBase):
    def __init__(self):
        super().__init__()
        self.admin_conn = init_opserver_conn()

    def make_agent_dict(self, agent):
        return {
            "host": agent.host,
            "started_at": agent.started_at,
            "status": agent.is_alive,
        }

    def get_agents(self):
        agents_dict = {}
        agents = self.admin_conn.network.agents(agent_type="Open vSwitch agent")
        for agent in agents:
            if "controller" not in agent.host:
                agents_dict[agent.host] = self.make_agent_dict(agent)

        return agents_dict

    def add_agent_db(self):
        agents = taasModel.NeutronAgent.query.all()
        cloud_agents = self.get_agents()

        for agent in agents:
            if agent.host in cloud_agents:
                cloud_agents.pop(agent.host)

        for host, c_agent in cloud_agents.items():
            obj = taasModel.NeutronAgent(host=host, started_at=c_agent["started_at"])
            taasModel.db.session.add(obj)
            taasModel.db.session.commit()

    def check_agent_start_time(self):
        cloud_agents = self.get_agents()

        for host, c_agent in cloud_agents.items():
            if c_agent["status"]:
                agent = taasModel.NeutronAgent.query.filter_by(host=host).first()
                if agent:
                    if agent.started_at != c_agent["started_at"]:
                        # 重新下发tap-flow
                        self.sync_flow(host)
                        taasModel.NeutronAgent.query.filter_by(host=host).update(
                            {"started_at": c_agent["started_at"]}
                        )
                else:
                    self.add_agent_db()
                    break
            else:
                current_app.logger.exception("%s Open vSwitch agent is down" % host)

    def sync_flow(self, host):
        flows = taasModel.TapFlow.query.filter_by(host=host).all()
        for flow in flows:
            self.conn.delete_flow(flow.flow_id)
            new_flow = self.conn.create_tap_flow(flow.source_port_id, flow.tap_service)[
                "tap_flow"
            ]
            taasModel.TapFlow.query.filter_by(flow_id=flow.flow_id).update(
                {"flow_id": new_flow["id"]}
            )
            taasModel.db.session.commit()
